﻿using MyProject.Core;
using MyProject.Entities.Config;
using MyProject.Services.ListItem;
using MyProject.Services.MyNavigation;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using UserEntity = MyProject.Entities.User.User;
using ValidationResult = System.ComponentModel.DataAnnotations.ValidationResult;
using TextBox = System.Windows.Controls.TextBox;
using Aspose.Cells;
using System.Reflection;
using Cell = Aspose.Cells.Cell;
using Passport = MyProject.Entities.Passport.Passport;
using System.Windows.Data;
using System.Windows.Controls.Primitives;
using System.Diagnostics.Contracts;
using MyProject.Entities.EmploymentContract;
using MyProject.Entities.Address;
using MyProject.Entities.ListItem;
using MyProject.Entities.ExcelColumns;

namespace MyProject.ViewsModel.UserList
{
    public class UserListContent
    {
        private readonly MyProject.Views.UserList.UserList _page = null;

        private readonly ObservableCollection<UserEntity> _dataSource = new ObservableCollection<MyProject.Entities.User.User>();
        public ListItemService lists { get; set; }
        public ICommand? InputCommand { get; } = null;

        public ObservableCollection<UserEntity> dataSource { get { return _dataSource; } }
        public UserListContent(MyProject.Views.UserList.UserList? page, MyNavigationService navigationService)
        {
            this._page = page;
            this.lists = ListItemService.getInstance();
            this.InputCommand = new RelayCommand(o => { this.InputCommandHandler(o); });
        }

        private void InputCommandHandler(object? commandType)
        {
            UserEntity? userData = this._page.DataGridSelectedItem();

            switch (commandType?.ToString())
            {
                case "addUser":
                    this.openUserDialog(new UserEntity());
                    break;
                case "editUser":
                    if (userData == null)
                    {
                        MessageBox.Show("Выберите элемент для редактирования в таблице!");
                        return;
                    }

                    this.openUserDialog(userData);

                    break;
                case "exportUsers":
                    this.ExportToExcel();
                    break;
                case "showChart":
                    this.OpenChartDialog();
                    break;
                case "deleteUser":
                    if (userData == null)
                    {
                        MessageBox.Show("Не выбрано элемента для удаления!");
                        return;
                    }

                    this.deleteUser(userData);
                    break;
                default:
                    MessageBox.Show("Данная команда не обработана");
                    return;
            }
        }

        private void openUserDialog(MyProject.Entities.User.User userData)
        {
            MyProject.Views.UserDialog.UserDialog dialog = new(userData);
            
            dialog.Closing += (object sender, System.ComponentModel.CancelEventArgs e) =>
            {
                if (dialog.DialogResult != null && dialog.DialogResult.IsSave)
                {
                    MyProject.Entities.User.User data = dialog.DialogResult.Data;

                    if (data.id != null && data.id > 0)
                    {
                        this.SaveChanges(data);
                    }
                    else
                    {
                        using (MyDbContext db = new MyDbContext())
                        {
                            db.User.Add(data);
                            db.SaveChanges();
                        }
                    }
                    
                    this.getTable();
                }
            };

            dialog.ShowDialog();
        }

        private void deleteUser(MyProject.Entities.User.User userData)
        {
            MessageBoxResult messageBoxResult = System.Windows.MessageBox.Show(
                "Вы уверены, что хотите удалить элемент?",
                 "Подтвердите действие",
                System.Windows.MessageBoxButton.YesNo);

            if (messageBoxResult == MessageBoxResult.Yes)
            {
                using (MyDbContext db = new MyDbContext())
                {

                    db.User.Remove(userData);
                    if (userData.passport != null)
                    {
                        db.Passport.Remove(userData.passport);
                    }

                    if (userData.contract != null)
                    {
                        db.EmploymentContract.Remove(userData.contract);
                    }

                    if (userData.registration != null)
                    {
                        db.Addresses.Remove(userData.registration);
                    }

                    if (userData.place_residence != null)
                    {
                        db.Addresses.Remove(userData.place_residence);
                    }

                    db.SaveChanges();

                }
                this.getTable();
            }
        }

        public async void getTable()
        {
            ObservableCollection<MyProject.Entities.User.User> collection = this._dataSource;

            await using (MyDbContext db = new MyDbContext())
            {
                collection.Clear();

                var list = from user in db.User
                            join passport in db.Passport on user.passportId equals passport.id
                            join contract in db.EmploymentContract on user.contractId equals contract.id
                            join place_residence in db.Addresses on user.place_residenceId equals place_residence.id
                            join registration in db.Addresses on user.registrationId equals registration.id
                            select new
                            {
                                user = user,
                                passport = passport,
                                contract = contract,
                                place_residence = place_residence,
                                registration = registration
                            };

                foreach (var row in list)
                {
                    UserEntity user = row.user;
                    user.passport = row.passport;
                    user.contract = row.contract;
                    user.place_residence = row.place_residence;

                    collection.Add(user);
                }
            }
        }

        public void CellEditEnding(object? sender, DataGridCellEditEndingEventArgs e)
        {

            string? val = null;
            string column = e.Column.SortMemberPath;
            UserEntity userData = (UserEntity)e.Row.Item;

            switch (e.EditingElement.GetType().ToString())
            {
                case "System.Windows.Controls.TextBox":
                    TextBox control = (TextBox)e.EditingElement;
                    val = control.Text;
                    break;
            }

            ExcelColumn.setPropValue(userData, column, val);

            List<ValidationResult> errorList = ValidationHelper.getFormErrors(userData);

            if (errorList != null)
            {
                e.Cancel = true;
                MessageBoxResult messageBoxResult = System.Windows.MessageBox.Show(
                    e.Column.Header + " - " + errorList.Last().ToString());
                return;
            }

            this.SaveChanges((UserEntity)e.Row.Item);

            this.getTable();
        }

        public void SaveChanges(UserEntity userData)
        {
            using (MyDbContext db = new MyDbContext())
            {
                db.User.Update(userData);
                db.SaveChanges();
            }
        }

        public void SelectionChanged (object sender, SelectionChangedEventArgs e)
        {

            if (e.RemovedItems.Count < 1)
            {
                return;
            }

            Control control = (Control)sender;
            object? value = null;
            string propName = control.Name;

            UserEntity userData = null;


            try
            {
                userData = (UserEntity)(control.DataContext);
            }
            catch (Exception er)
            {
                return;
            }


            switch (sender.GetType().ToString())
            {
                case "System.Windows.Controls.ComboBox":
                    ComboBox comboBox = (ComboBox)sender;
                    value = comboBox.SelectedValue;
                    break;
                case "System.Windows.Controls.DatePicker":
                    DatePicker datePicker = (DatePicker)sender;
                    value = datePicker.SelectedDate;
                    break;
            }


            userData.GetType().GetProperty(propName).SetValue(userData, value);


            List<ValidationResult> errorList = ValidationHelper.getFormErrors(userData);

            if (errorList != null)
            {
                MessageBoxResult messageBoxResult = System.Windows.MessageBox.Show(errorList.Last().ToString());
                this.getTable();
                return;
            }

            this.SaveChanges(userData);
            this.getTable();
        }

        private void ExportToExcel()
        {

            if (this.dataSource.Count == 0)
            {
                System.Windows.MessageBox.Show(
                "Внимание!",
                 "Нет данных для экспорта.",
                System.Windows.MessageBoxButton.OK);
                return;
            }


            var dialog = new Microsoft.Win32.SaveFileDialog();
            dialog.FileName = "Export";
            dialog.DefaultExt = ".xlsx";
            dialog.Filter = "Text documents (.xlsx)|*.xlsx";

            bool? result = dialog.ShowDialog();

            if (result == false)
            {
                return;
            }

            Workbook wb = new Workbook();

            Worksheet sheet = wb.Worksheets[0];

            Cells cells = sheet.Cells;

            DataGrid dataGrid = this._page.getDataGrid();

            Dictionary<int, string> flatList = this.lists.getFlatList();

            // Подготавливаем список свойств
            PropertyInfo[] props = typeof(UserEntity).GetProperties();
            PropertyInfo[] propsPass = typeof(Passport).GetProperties();
            PropertyInfo[] propsContract = typeof(EmploymentContract).GetProperties();
            PropertyInfo[] propsAddress = typeof(Address).GetProperties();

            Type MyTitle = typeof(MyTitleAttribute);
            List<ExcelColumn> columns = new List<ExcelColumn>();

            AppConfig appConfig = AppConfig.getInstance();

            int column = 0; 
            int row = 0;

            // Заголовок таблицы
            foreach (PropertyInfo col in props)
            {
                MyTitleAttribute colAttribute = (MyTitleAttribute)col.GetCustomAttribute(MyTitle);
                if (colAttribute == null)
                {
                    continue;
                }
                Cell header = null;
                switch (col.Name)
                {
                    case "passport":
                        foreach(PropertyInfo colPass in propsPass)
                        {
                            MyTitleAttribute colPassAttribute = (MyTitleAttribute)colPass.GetCustomAttribute(MyTitle);
                            if (colPassAttribute == null)
                            {
                                continue;
                            }
                            string path = $"{col.Name}.{colPass.Name}";

                            switch (colPass.Name)
                            {
                                case "date_of_issue":
                                    columns.Add(new ExcelColumn(colPass.Name, path, ExcelColumnsType.Date));
                                    break;
                                case "issued_by_whom_id":
                                    columns.Add(new ExcelColumn(colPass.Name, path, ExcelColumnsType.List));
                                    break;
                                default:
                                    columns.Add(new ExcelColumn(colPass.Name, path));
                                    break;
                            }

                            header = cells[CellsHelper.CellIndexToName(row, column)];
                            header.PutValue($"({colAttribute.Title})" + colPassAttribute.Title);
                            column++;
                        }
                        break;
                    case "contract":
                        foreach (PropertyInfo colC in propsContract)
                        {
                            MyTitleAttribute colCAttribute = (MyTitleAttribute)colC.GetCustomAttribute(MyTitle);
                            if (colCAttribute == null)
                            {
                                continue;
                            }
                            string path = $"{col.Name}.{colC.Name}";
                            switch (colC.Name)
                            {
                                case "creation_date":
                                case "employment_date":
                                    columns.Add(new ExcelColumn(colC.Name, path, ExcelColumnsType.Date));
                                    break;
                                default:
                                    columns.Add(new ExcelColumn(colC.Name, path));
                                    break;
                            }

                            header = cells[CellsHelper.CellIndexToName(row, column)];
                            header.PutValue($"({colAttribute.Title})" + colCAttribute.Title);
                            column++;
                        }
                        break;
                    case "place_residence":
                    case "registration":
                        foreach (PropertyInfo colA in propsAddress)
                        {
                            MyTitleAttribute colADAttribute = (MyTitleAttribute)colA.GetCustomAttribute(MyTitle);
                            if (colADAttribute == null)
                            {
                                continue;
                            }

                            string path = $"{col.Name}.{colA.Name}";

                            switch (colA.Name)
                            {
                                case "city_id":
                                case "street_id":
                                    columns.Add(new ExcelColumn(colA.Name, path, ExcelColumnsType.List));
                                    break;
                                default:
                                    columns.Add(new ExcelColumn(colA.Name, path));
                                    break;
                            }

                            header = cells[CellsHelper.CellIndexToName(row, column)];
                            header.PutValue($"({colAttribute.Title})" + colADAttribute.Title);
                            column++;
                        }
                        break;
                    default:
                        header = cells[CellsHelper.CellIndexToName(row, column)];
                        header.PutValue(colAttribute.Title);
                        columns.Add(new ExcelColumn(col.Name));
                        column++;
                        break;
                }

                
            }

            row++;

            // Тело таблицы
            foreach (UserEntity user in this.dataSource.ToList())
            {
                column = 0;

                foreach (ExcelColumn col in columns)
                {
                    Cell cell = cells[CellsHelper.CellIndexToName(row + 1, column)];
                    Column ColumnData = cells.Columns[column];
                    ColumnData.Width = 20;
                    column++;

                    string val = "";

                    switch(col.type)
                    {
                        case ExcelColumnsType.List:
                            val = flatList[(int)ExcelColumn.GetPropValue(user, col.path)];
                            break;
                        case ExcelColumnsType.Date:
                            DateTime? date = (DateTime)ExcelColumn.GetPropValue(user, col.path);
                            if (date != null)
                            {
                                val = $"{date:D}";
                            }

                            break;
                        default:
                              val = (String)ExcelColumn.GetPropValue(user, col.path)?.ToString();
                            break;
                    }

                    cell.PutValue(val);
                }
                row++;
            }

            Aspose.Cells.Tables.ListObject listObject = sheet.ListObjects[sheet.ListObjects.Add(CellsHelper.CellIndexToName(0, 0), CellsHelper.CellIndexToName(row, column - 1), true)];
            listObject.TableStyleType = Aspose.Cells.Tables.TableStyleType.TableStyleMedium10;

          
            sheet.Name = appConfig.AppTitle ?? "Лист 1";


            try
            {
                wb.Save(dialog.FileName, SaveFormat.Xlsx);
                System.Windows.MessageBox.Show("Экспорт завершен");
            } catch (Exception e)
            {
                System.Windows.MessageBox.Show(
                "Внимание!",
                 "Закройте файл и повторите попытку",
                System.Windows.MessageBoxButton.OK);
            }
           
        }

        public void OpenChartDialog()
        {
            MyProject.Views.Chart.ChartDialog dialog = new();
            dialog.ShowDialog();
        }

        public void GenerateDataGridColumns(DataGrid table)
        {
            PropertyInfo[] UserProps = typeof(UserEntity).GetProperties();
            PropertyInfo[] PassportProps = typeof(Passport).GetProperties();
            PropertyInfo[] ContractProps = typeof(EmploymentContract).GetProperties();
            PropertyInfo[] AddressProps = typeof(Address).GetProperties();

            Type AttrType = typeof(MyTitleAttribute);

            foreach (PropertyInfo prop in UserProps)
            {
                MyTitleAttribute colAttribute = (MyTitleAttribute)prop.GetCustomAttribute(AttrType);

                if (colAttribute == null)
                {
                    continue;
                }

                switch (prop.Name)
                {
                    case "id":
                    case "name":
                    case "surname":
                    case "patronymic":

                        DataGridTextColumn? columnn = new DataGridTextColumn();
                        columnn.Binding = new Binding(prop.Name);
                        columnn.Header = colAttribute?.Title;
                        switch (prop.Name)
                        {
                            case "id":
                                columnn.Width = 50;
                                columnn.IsReadOnly = true;
                                break;
                            default:
                                columnn.Width = 150;
                                break;
                        }
                        table.Columns.Add(columnn);

                        break;
                    case "passport":
                        foreach (PropertyInfo propPass in PassportProps)
                        {
                            MyTitleAttribute colAttributePass = (MyTitleAttribute)propPass.GetCustomAttribute(AttrType);

                            if(colAttributePass == null)
                            {
                                continue;
                            }

                            switch (propPass.Name)
                            {
                                case "series":
                                case "number":
                                    DataGridTextColumn? column = new DataGridTextColumn();
                                    column.Binding = new Binding(prop.Name + "." + propPass.Name);
                                    column.Header = $"({colAttribute.Title}) {colAttributePass.Title}";
                                    column.Width = 100;
                                    table.Columns.Add(column);
                                    break;
                                case "date_of_issue":
                                    DataGridTemplateColumn? dateColumn = new DataGridTemplateColumn();

                                    dateColumn.SortMemberPath = "Date";
                                    dateColumn.Header = $"({colAttribute.Title}) {colAttributePass.Title}";
                                    
                                    dateColumn.Width = 80;

                                    dateColumn.IsReadOnly = true;

                                    DataTemplate dataTemplate = new DataTemplate();
                                    FrameworkElementFactory datePicker = new FrameworkElementFactory(typeof(DatePicker));

                                    Binding binding = new Binding(prop.Name + "." + propPass.Name);
                                    binding.Mode = BindingMode.TwoWay;
                                    datePicker.Name = propPass.Name;
                                    datePicker.SetBinding(DatePicker.SelectedDateProperty, binding);
                                    
                                    dataTemplate.VisualTree = datePicker;
                                    dateColumn.CellTemplate = dataTemplate;
                                    table.Columns.Add(dateColumn);
                                    break;
                                case "issued_by_whom_id":
                                    DataGridTemplateColumn? cbColumn = new DataGridTemplateColumn();

                                    cbColumn.Header = $"({colAttribute.Title}) {colAttributePass.Title}";
                                    cbColumn.Width = 150;

                                    DataTemplate cbDataTemplate = new DataTemplate();

                                    FrameworkElementFactory comboBox = new FrameworkElementFactory(typeof(ComboBox));
                                    comboBox.Name = propPass.Name;

                                    Binding cbBinding = new Binding(prop.Name + "." + propPass.Name);
                                    cbBinding.Mode = BindingMode.TwoWay;
                                    comboBox.SetBinding(ComboBox.SelectedValueProperty, cbBinding);
                                    comboBox.SetValue(ComboBox.SelectedValuePathProperty, "id");
                                    comboBox.SetValue(ComboBox.ItemsSourceProperty, this.lists.Departments);
                                    comboBox.SetValue(ComboBox.IsReadOnlyProperty, true);

                                    cbDataTemplate.VisualTree = comboBox;
                                    cbColumn.CellTemplate = cbDataTemplate;
                                    table.Columns.Add(cbColumn);
                                    break;
                            }
                        }
                        break;
                    case "contract":
                        foreach (PropertyInfo propContract in ContractProps)
                        {
                            MyTitleAttribute colAttributeCon = (MyTitleAttribute)propContract.GetCustomAttribute(AttrType);

                            if (colAttributeCon == null)
                            {
                                continue;
                            }

                            switch (propContract.Name)
                            {
                                case "id":
                                    DataGridTextColumn? column = new DataGridTextColumn();
                                    column.Binding = new Binding(prop.Name + "." + propContract.Name);
                                    column.Header = $"({colAttribute.Title}) {colAttributeCon.Title}";
                                    column.Width = 100;
                                    column.IsReadOnly = true;
                                    table.Columns.Add(column);
                                    break;
                                case "creation_date":
                                case "employment_date":
                                    DataGridTemplateColumn? dateColumn = new DataGridTemplateColumn();

                                    dateColumn.SortMemberPath = "Date";
                                    dateColumn.Header = $"({colAttribute.Title}) {colAttributeCon.Title}";

                                    dateColumn.Width = 80;

                                    DataTemplate dataTemplate = new DataTemplate();
                                    FrameworkElementFactory datePicker = new FrameworkElementFactory(typeof(DatePicker));

                                    Binding binding = new Binding(prop.Name + "." + propContract.Name);
                                    binding.Mode = BindingMode.TwoWay;
                                    datePicker.Name = propContract.Name;
                                    datePicker.SetBinding(DatePicker.SelectedDateProperty, binding);
                                    dataTemplate.VisualTree = datePicker;
                                    dateColumn.CellTemplate = dataTemplate;
                                    table.Columns.Add(dateColumn);
                                    break;
                            }
                        }
                        break;
                    case "place_residence":
                    case "registration":
                        foreach (PropertyInfo propAddress in AddressProps)
                        {
                            MyTitleAttribute colAttributePass = (MyTitleAttribute)propAddress.GetCustomAttribute(AttrType);

                            if (colAttributePass == null)
                            {
                                continue;
                            }

                            switch (propAddress.Name)
                            {
                                case "apartment":
                                case "house":
                                case "index":
                                    DataGridTextColumn? column = new DataGridTextColumn();
                                    column.Binding = new Binding(prop.Name + "." + propAddress.Name);
                                    column.Header = $"({colAttribute.Title}) {colAttributePass.Title}";
                                    column.Width = 100;
                                    table.Columns.Add(column);
                                    break;
                                case "city_id":
                                    
                                    DataGridTemplateColumn? cbColumn = new DataGridTemplateColumn();

                                    cbColumn.Header = $"({colAttribute.Title}) {colAttributePass.Title}";
                                    cbColumn.Width = 150;

                                    DataTemplate cbDataTemplate = new DataTemplate();

                                    FrameworkElementFactory comboBox = new FrameworkElementFactory(typeof(ComboBox));
                                    comboBox.Name = propAddress.Name;

                                    Binding cbBinding = new Binding(prop.Name + "." + propAddress.Name);
                                    cbBinding.Mode = BindingMode.TwoWay;
                                    comboBox.SetBinding(ComboBox.SelectedValueProperty, cbBinding);
                                    comboBox.SetValue(ComboBox.SelectedValuePathProperty, "id");
                                    comboBox.SetValue(ComboBox.ItemsSourceProperty, this.lists.Cities);
                                    comboBox.SetValue(ComboBox.IsReadOnlyProperty, true);

                                    cbDataTemplate.VisualTree = comboBox;
                                    cbColumn.CellTemplate = cbDataTemplate;
                                    table.Columns.Add(cbColumn);
                                    break;
                                case "street_id":

                                    DataGridTemplateColumn? cbColumn1 = new DataGridTemplateColumn();

                                    cbColumn1.Header = $"({colAttribute.Title}) {colAttributePass.Title}";
                                    cbColumn1.Width = 150;

                                    DataTemplate cbDataTemplate1 = new DataTemplate();

                                    FrameworkElementFactory comboBox1 = new FrameworkElementFactory(typeof(ComboBox));
                                    comboBox1.Name = propAddress.Name;

                                    Binding cbBinding1 = new Binding(prop.Name + "." + propAddress.Name);
                                    cbBinding1.Mode = BindingMode.TwoWay;
                                    comboBox1.SetBinding(ComboBox.SelectedValueProperty, cbBinding1);
                                    comboBox1.SetValue(ComboBox.SelectedValuePathProperty, "id");
                                    comboBox1.SetValue(ComboBox.IsEditableProperty, false);

                                    List<ListItem> streets = new List<ListItem>();

                                    foreach(ListItem city in this.lists.Cities)
                                    {
                                        foreach (ListItem street in this.lists.Lists[(int)city.id])
                                        {
                                            streets.Add(street);
                                        }
                                    }

                                    comboBox1.SetValue(ComboBox.ItemsSourceProperty, streets);

                                    cbDataTemplate1.VisualTree = comboBox1;
                                    cbColumn1.CellTemplate = cbDataTemplate1;
                                    table.Columns.Add(cbColumn1);
                                    break;
                            }
                        }
                        break;
                }
            }
        }
    }
}
